﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Reflection;
using Ext.Net;

namespace KamLib
{

    public abstract class EntitiesListLogic<TListData, TEntities> : ListLogic<TListData>, IEntitiesContainer
        where TEntities : ObjectContext, new()
        where TListData : class, new()
    {
        public TEntities db;
        ObjectContext IEntitiesContainer.Entities { get { return db; } set { db = (TEntities)value; } }

        public override void Assign(ControlLogic src)
        {
            base.Assign(src);

            var sEntities = src as IEntitiesContainer;
            if (sEntities == null) return;
            var entities = sEntities.Entities as TEntities;
            if (entities != null) db = entities;
        }

        public override void Activate()
        {
            if (db == null)
                db = new TEntities();
        }

        public override void Dispose()
        {
            if (db == null) return;
            db.Dispose();
            db = null;
        }
    }

    public abstract class EntityListLogic<TListData, TEntities, TEntity> : EntitiesListLogic<TListData, TEntities>
        where TEntities : ObjectContext, new()
        where TListData : class, new()
        where TEntity : EntityObject, new()
    {
        protected static System.Linq.Expressions.Expression<Func<TEntity, TListData>> selectExpression;

        /// <summary>
        /// Использовать Filter(Key.MasterID) при получении данных
        /// </summary>
        public bool UseMaster { get { return Key.MasterID != null && Key.MasterID.Count > 0; } }

        /// <summary>
        /// Выражение вида:
        /// a => new FormData { Field1 = a.Parent.Field1, ... }
        /// Используется в Select для полей, которые берутся из других Entities
        /// </summary>
        protected static System.Linq.Expressions.Expression<Func<TEntity, TListData>> SelectInit;

        public override IEnumerable<TListData> SelectEntities(WhereArgs e)
        {
            return Select(e, SelectInit);
        }

        protected IEnumerable<TListData> Select(WhereArgs e, System.Linq.Expressions.Expression<Func<TEntity, TListData>> init)
        {
            Activate();
            return UseMaster
                ? db.Table<TEntity>().Filter(Key.MasterID).Select(ref selectExpression, init).Where(e)
                : db.Table<TEntity>().Select(ref selectExpression, init).Where(e);
        }

        public override void SaveLocalChanges(IEnumerable<object> newData)
        {
            var currentData = UseMaster
                ? db.Table<TEntity>().Filter(Key.MasterID).ToList()
                : db.Table<TEntity>().ToList();

            var keyCount = KeyFields.Length;
            var ekeyProps = EntityHelper.GetKeyFields<TEntity>();
            var keyProps = new MemberInfo[keyCount];

            for (var i = 0; i < keyCount; ++i)
            {
                keyProps[i] = typeof(TListData).GetPropertyOrField(KeyFields[i]);
            }

            foreach (var ndata in newData)
            {
                var isnew = true;
                for (var i = 0; i < keyCount; ++i)
                {
                    if (keyProps[i].GetValue(ndata, null) == null) continue;
                    isnew = false;
                    break;
                }

                TEntity cdata;

                if (isnew)
                    cdata = new TEntity();
                else
                {
                    var keys = new object[keyCount];
                    for (var i = 0; i < keyCount; ++i)
                    {
                        keys[i] = keyProps[i].GetValue(ndata, null);
                    }

                    var ind = currentData.IndexOf(a =>
                    {
                        if (a == null) return false;
                        for (var i = 0; i < keyCount; ++i)
                        {
                            var key = keys[i];
                            var ekey = ekeyProps[i].GetValue(a, null);
                            if (!Equals(key, ekey)) return false;
                        }
                        return true;
                    });

                    if (ind >= 0)
                    {
                        cdata = currentData[ind];
                        currentData[ind] = null;
                    }
                    else
                    {
                        isnew = true;
                        cdata = new TEntity();
                    }
                }


                if (Key.MasterID != null) cdata.CopyFrom(Key.MasterID);
                cdata.CopyFrom(ndata);

                if (isnew)
                    db.AddObject(db.TableProp<TEntity>().Name, cdata);

            }

            foreach (var cdata in currentData)
            {
                if (cdata != null)
                    db.DeleteObject(cdata);
            }

        }

    }

}